#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Keypad.h>
#include "ESP8266WiFi.h"
#include "config.h"
#include "Ticker.h"

#define SCREEN_WIDTH 128    // OLED display width, in pixels
#define SCREEN_HEIGHT 64    // OLED display height, in pixels
#define OLED_RESET -1       // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

const byte ROWS = 4; // four rows
const byte COLS = 3; // three columns
char keys[ROWS][COLS] = {
    {'1', '2', '3'},
    {'4', '5', '6'},
    {'7', '8', '9'},
    {'*', '0', '#'}};
byte rowPins[ROWS] = {5, 13, 12, 0}; // connect to the row pinouts of the kpd
byte colPins[COLS] = {4, 16, 2};     // connect to the column pinouts of the kpd
Keypad kpd = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

const char *ssid = "HYJ";
const char *password = "h103103103";
const char *host = "192.168.21.105";
const uint16_t port = 11000;
WiFiClient client;

typedef enum
{
    SYSTEM_STATUS_IDLE = 0,
    SYSTEM_STATUS_WAIT_VERIFY, //等待验证
    SYSTEM_STATUS_IS_USING,
    SYSTEM_STATUS_OCCUPIED //被占用
} SYSTEM_STATUS;
#define BODY_DECT digitalRead(14)
#define BODY_DETECTED 0
#define BODY_NOT_DETECTED 1
SYSTEM_STATUS sysStatus = SYSTEM_STATUS_IDLE;
String passwd, inputPasswd;
Ticker timerAlarm, timerSign;
int bodyDect, refreshFlag;
long alarmTime, signTime;
#define ALARM_TIME_LIMIT    (5 * 6)
#define SIGN_TIME_LIMIT    (30 * 6)
// #define ALARM_TIME_LIMIT (1)
// #define SIGN_TIME_LIMIT (2)

void UpdateOLED()
{
    if (sysStatus == SYSTEM_STATUS_IDLE)
    {
        display.clearDisplay();
        display.setCursor(10, 16);
        display.setTextSize(2);
        display.println("Press '#' to verify");
        display.display();
        display.startscrollright(0x00, 0x0F);
    }
    if (sysStatus == SYSTEM_STATUS_WAIT_VERIFY)
    {
        display.clearDisplay();
        display.setCursor(0, 16);
        display.setTextSize(1);
        display.stopscroll();
        display.println("Input your code");
        display.println(inputPasswd);
        display.display();
    }
    if (sysStatus == SYSTEM_STATUS_IS_USING)
    {
        display.clearDisplay();
        display.setCursor(0, 0);
        display.stopscroll();
        display.setTextSize(2);
        display.println("The seat is being used");
        display.setCursor(0, 48);
        display.setTextSize(1);
        display.println("Press '#' to sign out");
        display.display();
    }
    if (sysStatus == SYSTEM_STATUS_OCCUPIED)
    {
        display.clearDisplay();
        display.setCursor(0, 16);
        display.stopscroll();
        display.setTextSize(2);
        display.println("Illegal Occupation");
        display.display();
    }
}

void timerSignCallback()
{
    String str = "sign time " + String(signTime);
    client.write(str.c_str(), str.length());
    signTime++;
    if (signTime >= SIGN_TIME_LIMIT)
    {
        String str = "checked out success";
        client.write(str.c_str(), str.length());
        timerSign.detach();
        sysStatus = SYSTEM_STATUS_IDLE;
        refreshFlag = 1;
    }
}

void timerAlarmCallback()
{
    String str = "alarm time " + String(alarmTime);
    client.write(str.c_str(), str.length());
    alarmTime++;
    if (alarmTime >= ALARM_TIME_LIMIT && sysStatus != SYSTEM_STATUS_OCCUPIED)
    {
        timerAlarm.detach();
        sysStatus = SYSTEM_STATUS_OCCUPIED;
        refreshFlag = 1;
    }
}

void setup()
{
    Wire.begin(3, 1);
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);

    if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS))
    {
        Serial.print("Init error ");
        for (;;)
            ; // Don't proceed, loop forever
    }
    delay(2000); // Pause for 2 seconds
    display.clearDisplay();
    display.display();

    display.setTextSize(1);              // Normal 1:1 pixel scale
    display.setTextColor(SSD1306_WHITE); // Draw white text
    display.setCursor(0, 0);             // Start at top-left corner
    display.println("init ok");
    display.display();
    delay(2000); // Pause for 2 seconds

    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        display.print(".");
        display.display();
    }

    display.clearDisplay();
    display.setCursor(0, 0); // Start at top-left corner
    display.print("Connected to ");
    display.println(ssid);
    display.println("IP address: ");
    display.println(WiFi.localIP());
    display.display();

    while (!client.connect(host, port))
    {
        display.println("connecting TCP Server");
        display.display();
        delay(2000);
    }
    if (client.connected())
    {
        display.println("client connected");
        display.display();
    }
    UpdateOLED();
}

void loop()
{
    if (refreshFlag)
    {
        if (signTime >= SIGN_TIME_LIMIT)
        {
            display.clearDisplay();
            display.setCursor(0, 16);
            display.stopscroll();
            display.setTextSize(2);
            display.println("AUTO SIGN OUT");
            display.display();
            delay(2000);
        }
        UpdateOLED();
        refreshFlag = 0;
    }
    if (kpd.getKeys())
    {
        for (int i = 0; i < LIST_MAX; i++) // Scan the whole key list.
        {
            if (kpd.key[i].stateChanged) // Only find keys that have changed state.
            {
                if (sysStatus == SYSTEM_STATUS_IDLE && kpd.key[i].kchar == '#' && kpd.key[i].kstate == RELEASED)
                {
                    sysStatus = SYSTEM_STATUS_WAIT_VERIFY;
                    UpdateOLED();
                }
                else if (sysStatus == SYSTEM_STATUS_WAIT_VERIFY)
                {
                    if ((kpd.key[i].kchar >= '0' && kpd.key[i].kchar <= '9') && kpd.key[i].kstate == RELEASED)
                    {
                        inputPasswd += kpd.key[i].kchar;
                        if (inputPasswd.length() >= 6)
                        {
                            if (inputPasswd.equals(passwd))
                            {
                                sysStatus = SYSTEM_STATUS_IS_USING;
                                String str = "checked in success";
                                client.write(str.c_str(), str.length());
                            }
                            else
                            {
                                inputPasswd.clear();
                            }
                        }
                        UpdateOLED();
                    }
                }
                if (sysStatus == SYSTEM_STATUS_IS_USING && kpd.key[i].kchar == '#' && kpd.key[i].kstate == RELEASED)
                {
                    sysStatus = SYSTEM_STATUS_IDLE;
                    String str = "checked out success";
                    client.write(str.c_str(), str.length());
                    UpdateOLED();
                }
                String msg;
                switch (kpd.key[i].kstate)
                { // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
                case PRESSED:
                    msg = " PRESSED.";
                    break;
                case HOLD:
                    msg = " HOLD.";
                    break;
                case RELEASED:
                    msg = " RELEASED.";
                    break;
                case IDLE:
                    msg = " IDLE.";
                }
                String str = "Key " + String(kpd.key[i].kchar) + msg;
                client.write(str.c_str(), str.length());
            }
        }
    }

    while (!client.connected())
    {
        display.setCursor(0, 0); // Start at top-left corner
        display.clearDisplay();
        if (!client.connect(host, port))
        {
            display.println("wait connect to server ......");
            display.display();
        }
    }

    if (client.connected()) //当处于链接状态时
    {
        if (client.available()) //如果缓冲区字符串大于0
        {
            String line = client.readStringUntil('\r\n'); //获取字符串
            // client.print(String("got it ") + line.c_str()); //向服务器反馈信息
            if (!line.isEmpty())
            {
                int pos = line.indexOf("Passwd:");
                if (pos != -1)
                {
                    passwd = line.substring(pos + strlen("Passwd:"));
                }
            }
        }
    }

    if (bodyDect != BODY_DECT)
    {
        bodyDect = BODY_DECT;
        if (bodyDect == BODY_DETECTED)
        {
            if (timerSign.active())
                timerSign.detach();
            if (sysStatus == SYSTEM_STATUS_IDLE)
            {
                timerAlarm.attach(10, timerAlarmCallback);
                alarmTime = 0;
            }
        }
        if (bodyDect == BODY_NOT_DETECTED)
        {
            if (timerAlarm.active())
                timerAlarm.detach();
            if (sysStatus == SYSTEM_STATUS_IS_USING)
            {
                timerSign.attach(10, timerSignCallback);
                signTime = 0;
            }
            if (sysStatus == SYSTEM_STATUS_OCCUPIED)
            {
                sysStatus = SYSTEM_STATUS_IDLE;
                UpdateOLED();
            }
        }
    }
}
